//@author A0081058H
package net.mysoc.w111j.ui.main;

import javafx.stage.Stage;
import net.mysoc.w111j.model.Expense;
import net.mysoc.w111j.model.Money;
import net.mysoc.w111j.service.CommandService;
import net.mysoc.w111j.service.ExpenseService;
import net.mysoc.w111j.ui.expensedetail.ExpenseDetailPresenter;
import net.mysoc.w111j.ui.expenselist.ExpenseListPresenter;
import net.mysoc.w111j.ui.generatereport.GenerateReportPresenter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The presenter for the main application.
 */
public class MainPresenter {
    private static Logger logger = LoggerFactory.getLogger(MainPresenter.class);

    private final MainModel model;
    private final MainView view;
    private final Stage primaryStage;
    private ExpenseDetailPresenter expenseDetailPresenter;
    private ExpenseListPresenter expenseListPresenter;
    private GenerateReportPresenter generateReportPresenter;
    private final ExpenseService expenseService;
    private CommandService commandService;

    /**
     * Constructor. Links the application's {@code MainModel} and {@code MainView} to this {@code MainPresenter}.
     *
     * @param model the {@code MainModel} to link to this {@code MainPresenter}.
     * @param view the {@code MainView} to link to this {@code MainPresenter}.
     * @param primaryStage the {@code Stage} this {@code MainPresenter} is on.
     * @param expenseService the {@code ExpenseService} to link to this {@code MainPresenter}.
     */
    public MainPresenter(MainModel model, MainView view, Stage primaryStage, ExpenseService expenseService) {
        this.model = model;
        this.view = view;
        this.primaryStage = primaryStage;
        this.expenseService = expenseService;
        this.view.setPresenter(this);
    }

    /**
     * Getter for the {@code MainPresenter}'s current view.
     *
     * @return the current {@code MainView}
     */
    public MainView getView() {
        return this.view;
    }

    /**
     * Sets the default view for the application.
     */
    public void setDefaultView() {
        refresh();
        this.model.setContent(this.expenseListPresenter.getView());
    }

    /**
     * Sets the {@code CommandService}.
     *
     * @param commandService
     */
    public void setCommandService(CommandService commandService) {
        this.commandService = commandService;
    }

    /**
     * Sets the {@code ExpenseDetailPresenter} and links it to this {@code MainPresenter}.
     *
     * @param expenseDetailPresenter
     */
    public void setExpenseDetailPresenter(ExpenseDetailPresenter expenseDetailPresenter) {
        this.expenseDetailPresenter = expenseDetailPresenter;
        this.expenseDetailPresenter.setMainPresenter(this);
    }

    /**
     * Sets the {@code ExpenseListPresenter}.
     *
     * @param expenseListPresenter
     */
    public void setExpenseListPresenter(ExpenseListPresenter expenseListPresenter) {
        this.expenseListPresenter = expenseListPresenter;
    }

    /**
     * Getter for the set {@code ExpenseListPresenter}.
     *
     * @return the set {@code ExpenseListPresenter}.
     */
    public ExpenseListPresenter getExpenseListPresenter() {
        return this.expenseListPresenter;
    }

    /**
     * Sets the {@code GenerateReportPresenter}.
     *
     * @param generateReportPresenter
     */
    public void setGenerateReportPresenter(GenerateReportPresenter generateReportPresenter) {
        this.generateReportPresenter = generateReportPresenter;
    }

    /**
     * Getter for the set {@code GenerateReportPresenter}.
     *
     * @return the set {@code GenerateReportPresenter}.
     */
    public GenerateReportPresenter getGenerateReportPresenter() {
        return this.generateReportPresenter;
    }

    /**
     * Links the "Generate Report" button in the {@code MainView} to {@code ExpenseDetailPresenter}.
     *
     */
    public void handleGenerateReport() {
        logger.debug("Handle generate report");
        this.generateReportPresenter.showDialog(this.primaryStage);
    }

    /**
     * Links the "Add Expense" button in the {@code MainView} to {@code ExpenseDetailPresenter}.
     *
     */
    public void handleAddExpense() {
        logger.debug("Add expense");
        this.expenseDetailPresenter.setExpense(null);
        this.expenseDetailPresenter.showDialog(this.primaryStage);
    }

    /**
     * Sets up the {@code ExpenseDetailPresenter} for editing an {@code Expense} and refreshes the view.
     *
     * @param expense the {@code Expense} to be edited.
     */
    public void handleEditExpense(Expense expense) {
        logger.debug("Edit expense");
        this.expenseDetailPresenter.setExpense(expense);
        this.expenseDetailPresenter.showDialog(this.primaryStage);
        refreshAll();
    }

    /**
     * Links the command line in the {@code MainView} to the {@code CommandService}.
     *
     */
    public void handleCommandLineInput() {
        logger.info("TODO: Handle command-line input - {}", model.getCommandLine());
        commandService.parseCommand(model.getCommandLine());
        refreshAll();
    }

    /**
     * Links the search box in the {@code MainView} to {@code ExpenseListPresenter}.
     */
    public void handleSearchFieldInput() {
        // We don't use a property so the command line can call this.
        logger.debug("Handle search field input - {}", this.model.getSearchField());
        this.expenseListPresenter.handleSearchBoxInput(this.model.getSearchField());
    }

    /**
     * Links the undo button in the {@code MainView} to the application{@code ExpenseService},
     */
    public void handleUndoButton() {
        expenseService.undo();
        refreshAll();
    }

    /**
     * Links the budget field in the {@code MainView} to the application {@code ExpenseService}
     */
    public void handleBudgetFieldInput() {
        Money amount = new Money(model.getBudget());
        expenseService.setBudget(amount);
        refresh();
    }

    /**
     * Gets the current user's budget from {@code ExpenseService}.
     *
     * @return the current budget.
     */
    public Money handleGetUserBudget() {
        return expenseService.getBudget();
    }

    /**
     * Handles keyboard shortcut for delete selected expense.
     */
    public void handleDeleteSelectedExpense() {
        this.expenseListPresenter.handleDeleteSelectedExpense();
    }

    /**
     * Handles keyboard shortcut for edit selected expense.
     */
    public void handleEditSelectedExpense() {
        this.expenseListPresenter.handleEditSelectedExpense();
    }

    /**
     * Focuses the command line textfield.
     */
    public void handleGotoCommandLine() {
        this.view.focusCommandLine();
    }

    /**
     * Focuses the search textfield.
     */
    public void handleGotoSearchField() {
        this.view.focusSearchField();
    }

    /**
     * Focuses the budget textfield.
     */
    public void handleGotoBudgetField() {
        this.view.focusBudgetField();
    }

    /**
     * Refreshes UI elements in this {@code MainView} by reloading (updated) data from the service.
     */
    public void refresh() {
        // Budget
        this.model.setBudget(expenseService.getBudget().toPlainString());
        // Current month expenditure
        this.model.setExpenditure(expenseService.getCurrentMonthExpenditure().toPlainString());
        // Undo button
        this.model.setUndoButtonDisable(!expenseService.canUndo());
        // Status
        this.model.setStatus(expenseService.getStatus());
    }

    /**
     * Refreshes all not-on-demand (refresh) elements
     */
    public void refreshAll() {
        this.refresh();
        this.expenseListPresenter.refresh();
    }
}
